package gang.org.springframework.framework.metadata;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

abstract class GangAnnotationsScanner
{

    private static final Map<AnnotatedElement, Annotation[]> declaredAnnotationCache =
            new ConcurrentHashMap<>(256);


    /**
     * TODO
     * @param context 实际的入参值，String -> gang.org.springframework.boot.autoconfigure.GImport
     *                            String -> gang.org.springframework.framework.annotation.GComponentScan
     * @param source 原始class类，E.g {@link gang.org.springframework.boot.autoconfigure.GEnableAutoConfiguration}
     *
     * */
    static<C,R> R scan(C context, AnnotatedElement source, GangMergedAnnotations.SearchStrategy searchStrategy,GangAnnotationsProcessor<C,R> processor)
    {
        /*
         * GangMergedAnnotation -> GangTypeMappedAnnotation
          */
        R result = process(context,source,searchStrategy,processor);

        return processor.finish(result);
    }

    /**
     * TODO
     * @param context String value -> gang.org.springframework.boot.autoconfigure.GImport
     *                String value -> gang.org.springframework.framework.annotation.GComponentScan
     * @param source Class Type {@link gang.org.springframework.boot.autoconfigure.GEnableAutoConfiguration}
     * */
    private static<C,R> R process(C context, AnnotatedElement source, GangMergedAnnotations.SearchStrategy searchStrategy, GangAnnotationsProcessor<C,R> processor)
    {
        //R -> GangTypeMappedAnnotation
        if (source instanceof Class){
            R result = processClass(context,(Class<?>)source,searchStrategy,processor);
            return result;
        }
        if (source instanceof Method){
            //TODO source instanceof Method
        }
        return null;
    }

    /**
     * TODO
     * @param context String type value -> gang.org.springframework.boot.autoconfigure.GImport
     *                String type value -> gang.org.springframework.framework.annotation.GComponentScan
     * @param source Class type {@link gang.org.springframework.boot.autoconfigure.GEnableAutoConfiguration}
     * */
    private static <C,R> R processClass(C context, Class<?> source, GangMergedAnnotations.SearchStrategy searchStrategy, GangAnnotationsProcessor<C,R> processor)
    {
        //R -> GangTypeMappedAnnotation
        switch (searchStrategy){
            case DIRECT:
                return null;
            case INHERITED_ANNOTATIONS:
                return processClassInheritedAnnotations(context,source,searchStrategy,processor);
            case SUPERCLASS:
                return null;
            case TYPE_HIERARCHY:
                return null;
            case TYPE_HIERARCHY_AND_ENCLOSING_CLASSES:
                return null;
            default:
                return null;
        }
    }


    /**
     * @param context value is String{@link String} gang.org.springframework.boot.autoconfigure.GImport
     *                         String type value -> gang.org.springframework.framework.annotation.GComponentScan
     * @param source {@link gang.org.springframework.boot.autoconfigure.GEnableAutoConfiguration}
     * */
    //TODO
    private static <C,R> R processClassInheritedAnnotations(C context, Class<?> source, GangMergedAnnotations.SearchStrategy searchStrategy, GangAnnotationsProcessor<C,R> processor)
    {
        // R -> GangTypeMappedAnnotation
        try {
            if (isWithoutHierarchy(source, searchStrategy)) {
                return processElement(context, source, processor);
            }

            Class<?> root = source;

            Annotation[] declaredAnnotations = getDeclaredAnnotations(source,true);

            R result = processor.doWithAnnotations(context,0,source,declaredAnnotations);
            return result;
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }

    /**
     * TODO
     * @param source {@link gang.org.springframework.boot.autoconfigure.GEnableAutoConfiguration}
     * @param defensive default value is true
     *                  业务意义，过滤原生的JAVA注解
     * */
    static Annotation[] getDeclaredAnnotations(AnnotatedElement source, boolean defensive) {

        /**
         * TODO
         * 类上没有注解，会出现空指针，真实没有该逻辑，当前调式
         * */
        if (null == source) {
            return null;
        }

        boolean cached = false;

        Annotation[] annotations = declaredAnnotationCache.get(source);

        if (annotations != null) {
            cached = true;
        }

        /**
         * 获取注解类上的注解
         * 1）原注解Configuration
         * 2）获取到的注解为：@Target/@Retention/@Documented/@Component
         * */
        Annotation[] declaredAnnotations = source.getDeclaredAnnotations();

        for (int i =0; i<declaredAnnotations.length; i++)
        {
            Annotation annotation = declaredAnnotations[i];
            Class<? extends Annotation> clazz = annotation.annotationType();
            String name = clazz.getName();
            if (name.startsWith("java"))
            {
                declaredAnnotations[i] = null;
            }
        }

        return declaredAnnotations;
    }

    private static boolean isWithoutHierarchy(AnnotatedElement source, GangMergedAnnotations.SearchStrategy searchStrategy){
        if (source == Object.class){
            return true;
        }
        if (source instanceof Class) {
            Class<?> sourceClass = (Class<?>) source;
            boolean isObject = sourceClass.getSuperclass() == Object.class;
            boolean isZero = sourceClass.getInterfaces().length == 0;

            boolean noSuperTypes = isObject && isZero;

            boolean var1 = noSuperTypes && sourceClass.getEnclosingClass()== null;

            return searchStrategy == GangMergedAnnotations.SearchStrategy.TYPE_HIERARCHY_AND_ENCLOSING_CLASSES ? var1 : noSuperTypes;
        }
        if (source instanceof Method){
            //TODO
        }
        return true;
    }

    private static <C, R> R processElement(C context, AnnotatedElement source, GangAnnotationsProcessor<C,R> processor){
        try {
            R result = processor.doWithAggregate(context, 0);
            return result != null ? result : processor.doWithAnnotations(context, 0,source, getDeclaredAnnotations(source, false));
        }catch (Throwable ex){
            ex.printStackTrace();
        }

        return null;
    }
}
